package com.xbongbong.paas.service.impl;

import com.xbongbong.paas.config.FileConfig;
import com.xbongbong.paas.service.AudioFileService;
import com.xbongbong.paas.service.ssl.MyX509TrustManager;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.MD5Util;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.service.aliyun.AliyunOssService;
import javazoom.jl.decoder.Bitstream;
import javazoom.jl.decoder.BitstreamException;
import javazoom.jl.decoder.Header;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

/**
 * @author youli.chen
 * @version v1.0
 * @description: 录音文件实现类
 * @date 2019/3/13 16:56
 * @since v1.0
 */
@Service("audioFileService")
public class AudioFileServiceImpl implements AudioFileService {

    private static final Logger LOG = LoggerFactory.getLogger(AudioFileServiceImpl.class);

    @Resource
    private AliyunOssService aliyunOssService;

    /**
     * 获取远程mp3文件的时长
     *
     * @param url mp3文件的url
     * @return 时长（单位：秒）
     * @throws XbbException 封装的业务异常
     * @author 章华隽
     * 创建时间：2018/1/10 下午2:06
     * 修改时间：
     * @version 3.17
     * @since 3.17
     */
    @Override
    public int getMp3TimeSeconds(String url) throws XbbException {
        URL urlFile;
        try {
            urlFile = new URL(url);
        } catch (MalformedURLException e) {
            LOG.error("mp3文件的地址并非URL（%s）：%s", url, e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100084);
        }
        int byteLength;
        BufferedInputStream bis;
        try {
            String httpHeader = "http://";
            String httpsHeader = "https://";
            if (url.startsWith(httpHeader)) {
                URLConnection conn = urlFile.openConnection();
                // 得到音乐文件的总长度
                byteLength = conn.getContentLength();
                bis = new BufferedInputStream(conn.getInputStream());
            } else if (url.startsWith(httpsHeader)) {
                HttpsURLConnection conn = (HttpsURLConnection) urlFile.openConnection();
                //设置超时间为5秒
                conn.setConnectTimeout(5000);
                //防止屏蔽程序抓取而返回403错误
                conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");

                // 创建SSLContext对象，并使用我们指定的信任管理器初始化
                TrustManager[] tm;
                tm = new TrustManager[]{new MyX509TrustManager()};
                SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
                sslContext.init(null, tm, new java.security.SecureRandom());
                // 从上述SSLContext对象中得到SSLSocketFactory对象
                SSLSocketFactory ssf = sslContext.getSocketFactory();

                conn.setSSLSocketFactory(ssf);

                // 得到音乐文件的总长度
                byteLength = conn.getContentLength();
                bis = new BufferedInputStream(conn.getInputStream());
            } else {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100085);
            }
        } catch (Exception e) {
            LOG.error("读取mp3的字节流出错（%s）：%s", url, e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100086);
        }
        if (byteLength == 0) {
            return 0;
        }
        Bitstream bt = new Bitstream(bis);
        Header header;
        try {
            header = bt.readFrame();
        } catch (BitstreamException e) {
            LOG.error("读取mp3的文件头出错（%s）：%s", url, e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100087);
        }
        int time = (int) header.total_ms(byteLength);
        time = time / 1000;
        LOG.info("读取到mp3的时长（%ss）地址（%s）", time, url);
        return time;
    }

    /**
     * 从网络的Url中下载文件，自动判断请求方式是http还是https
     *
     * @param urlStr   路径地址
     * @param fileName 文件名
     * @param savePath 存储路径
     * @return String 文件路径
     * @throws IOException io异常
     * @author 章华隽
     * 创建时间：2018/1/11 下午14:10
     * 修改时间：
     * @version 3.17
     * @since 3.17
     */
    @Override
    public String downloadFromUrl(String urlStr, String fileName, String savePath) throws Exception {
        String httpHeader = "http://";
        String httpsHeader = "https://";
        if (urlStr.startsWith(httpHeader)) {
            return downloadFromUrlHttp(urlStr, fileName, savePath);
        } else if (urlStr.startsWith(httpsHeader)) {
            return downloadFromUrlHttps(urlStr, fileName, savePath);
        } else {
            throw new IOException(I18nStringConstant.NOT_HTTP_OR_HTTPS_REQUEST);
        }
    }

    /**
     * 从网络HTTP的Url中下载文件
     *
     * @param urlStr   路径地址
     * @param fileName 文件名
     * @param savePath 存储路径
     * @return String 文件路径
     * @throws IOException io异常
     * @author 章华隽
     * 创建时间：2018/1/11 上午10:21
     * 修改时间：
     * @version 3.17
     * @since 3.17
     */
    private String downloadFromUrlHttp(String urlStr, String fileName, String savePath) throws IOException {
        URL url = new URL(urlStr);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        //设置超时间为5秒
        conn.setConnectTimeout(5000);
        //防止屏蔽程序抓取而返回403错误
        conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");

        return downloadFromInputStream(conn.getInputStream(), fileName, savePath);
    }

    /**
     * 从网络HTTPS的Url中下载文件
     *
     * @param urlStr   路径地址
     * @param fileName 文件名
     * @param savePath 存储路径
     * @return String 文件路径
     * @throws IOException io异常
     * @author 章华隽
     * 创建时间：2018/1/11 上午10:21
     * 修改时间：
     * @version 3.17
     * @since 3.17
     */
    private String downloadFromUrlHttps(String urlStr, String fileName, String savePath) throws Exception {
        URL url = new URL(urlStr);
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
        //设置超时间为5秒
        conn.setConnectTimeout(5000);
        //防止屏蔽程序抓取而返回403错误
        conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");

        // 创建SSLContext对象，并使用我们指定的信任管理器初始化
        TrustManager[] tm;
        tm = new TrustManager[]{new MyX509TrustManager()};
        SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
        sslContext.init(null, tm, new java.security.SecureRandom());
        // 从上述SSLContext对象中得到SSLSocketFactory对象
        SSLSocketFactory ssf = sslContext.getSocketFactory();

        conn.setSSLSocketFactory(ssf);

        return downloadFromInputStream(conn.getInputStream(), fileName, savePath);
    }

    /**
     * 从网络Url中下载文件
     *
     * @param inputStream 根据链接得到的输入流
     * @param fileName    文件名
     * @param savePath    存储路径
     * @return String 文件路径
     * @throws IOException io异常
     * @author 章华隽
     * 创建时间：2018/1/11 上午10:21
     * 修改时间：
     * @version 3.17
     * @since 3.17
     */
    private String downloadFromInputStream(InputStream inputStream, String fileName, String savePath) throws IOException {
        //获取自己数组
        byte[] getData = readInputStream(inputStream);

        //文件保存位置
        File saveDir = new File(savePath);
        if (!saveDir.exists()) {
            saveDir.mkdir();
        }

        String filePath = saveDir + File.separator + fileName;
        File file = new File(filePath);
        checkFileExists(file);

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
        } catch (IOException e) {
            e.printStackTrace();
            LOG.error("AudioFileServiceImpl.downloadFromInputStream IOException error");
            return null;
        } finally {
            if (fos != null) {
                try {
                    fos.write(getData);
                    fos.close();
                } catch (IOException e) {
                    LOG.error("AudioFileServiceImpl.downloadFromInputStream finally Recycling IOException error");
                }
            }
        }

        inputStream.close();
        LOG.info("下载成功：本地路径（%s/%s）", savePath, fileName);
        return filePath;
    }

    /**
     * 将本地文件上传至阿里云
     *
     * @param filePath 文件路径
     * @return 阿里云Oss存储地址
     * @author 章华隽
     * 创建时间：2018/1/26 下午2:05
     * 修改时间：
     * @version 3.17
     * @since 3.17
     */
    @Override
    public String upload2AliyunOss(String corpid, String filePath) {
        byte[] bytes = toByteArray(filePath);
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        String fileName = filePath.substring(filePath.lastIndexOf(File.separator) + 1, filePath.length());
        String fileKey = FileConfig.getFileKey(corpid, fileName, MD5Util.md5(bytes));
        return aliyunOssService.upload(fileKey, bytes);
    }

    /**
     * 将文件转化为字节流用于传递文件
     *
     * @param filePath 文件路径
     * @return 文件直接流
     * @author 章华隽
     * 创建时间：2018/1/25 下午5:51
     * 修改时间：
     * @version 3.17
     * @since 3.17
     */
    private byte[] toByteArray(String filePath) {
        InputStream in = null;
        try {
            in = new FileInputStream(filePath);
        } catch (FileNotFoundException e) {
            LOG.error("文件路径（%s）有误，并非正常文件：%s", filePath, e);
            return null;
        }

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024 * 4];
        int n;
        byte[] bytes;
        try {
            while ((n = in.read(buffer)) != -1) {
                out.write(buffer, 0, n);
            }
            bytes = out.toByteArray();
        } catch (IOException e) {
            LOG.error("文件路径（%s）引起了IO异常：%s", filePath, e);
            return null;
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    LOG.error("AudioFileServiceImpl.toByteArray finally Recycling IOException error");
                }
            }
        }
        return bytes;
    }

    /**
     * 判断文件夹是否存在，没有则新建
     *
     * @param path 文件夹路径
     * @author 章华隽
     * 创建时间：2018/1/11 上午11:00
     * 修改时间：
     * @version 3.17
     * @since 3.17
     */
    @Override
    public void checkDirExists(String path) {
        File dir = new File(path);
        if (dir.exists()) {
            if (dir.isDirectory()) {
                LOG.info("文件夹已经存在，%s", path);
            } else {
                LOG.info("存在同名文件，无法创建文件夹，%s", path);
            }
        } else {
            LOG.info("文件夹不存在，创建%s", path);
            if (dir.mkdirs()) {
                LOG.info("新建文件夹成功，%s", path);
            } else {
                LOG.error("新建文件夹失败，%s", path);
            }
        }
    }

    /**
     * 判断文件是否存在，没有则创建
     *
     * @param file 文件
     * @author 章华隽
     * 创建时间：2018/1/11 上午11:00
     * 修改时间：
     * @version 3.17
     * @since 3.17
     */
    private void checkFileExists(File file) {
        if (file.exists()) {
            LOG.info("文件已经存在，%s", file.getPath());
        } else {
            LOG.info("文件不存在，创建%s", file.getPath());
            try {
                if (file.createNewFile()) {
                    LOG.info("创建文件成功，%s", file.getPath());
                } else {
                    LOG.error("创建文件失败，%s", file.getPath());
                }
            } catch (IOException e) {
                LOG.error("创建文件异常，%s", file.getPath());
                e.printStackTrace();
            }
        }
    }

    /**
     * 从输入流中获取字节数组
     *
     * @param inputStream 输入流
     * @return 得到的字节数组
     * @throws IOException io异常
     * @author 章华隽
     * 创建时间：2018/1/11 上午10:18
     * 修改时间：
     * @version 3.17
     * @since 3.17
     */
    private byte[] readInputStream(InputStream inputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int len;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        while ((len = inputStream.read(buffer)) != -1) {
            bos.write(buffer, 0, len);
        }
        bos.close();
        return bos.toByteArray();
    }
}
